package externalSort.Tournament;

import java.io.BufferedWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class bufferWriteManager implements Runnable {

    public List<bufferSimulator> buffers;
    public Semaphore bufferAvailable;
    public BufferedWriter bufferedWriter;
    public Lock bufferLock;

    private bufferSimulator bufferUsing;
    private final BlockingQueue<bufferSimulator> queue;
    private final AtomicBoolean finish;

    private final Semaphore canPoll;

    public bufferWriteManager(int bufferSize) {
        this.buffers = new ArrayList<>(5);
        for (int i = 0; i < 5; i++) {
            bufferSimulator cur = new bufferSimulator(bufferSize);
            cur.setState(BufferState.IDLE);
            buffers.add(cur);
        }
        this.bufferAvailable = new Semaphore(2);
        this.bufferLock = new ReentrantLock();
        this.queue = new LinkedBlockingQueue<>();
        this.finish = new AtomicBoolean(false);

        this.canPoll=new Semaphore(0);
    }

    public BufferedWriter getBufferedWriter() {
        return bufferedWriter;
    }

    public void setBufferedWriter(BufferedWriter bufferedWriter) {
        this.bufferedWriter = bufferedWriter;
    }

    /**
     * 主线程：往当前buffer中写入数字
     * 若buffer满，则将其加入写队列
     */
    public void write(int num) throws Exception {
        if (num == Integer.MAX_VALUE) {
//            System.out.println("[bufferWrite] AT write : flush");
            flushRemaining();
            finish.set(true); // 通知run()线程结束
            canPoll.release();
            return;
        }

        if(bufferUsing==null){
//            System.out.println("[bufferWrite] AT write : 尝试获取bufferAvailable");
            bufferAvailable.acquire();
//            System.out.println("[bufferWrite] AT write : 成功获取bufferAvailable");
            bufferLock.lock();
            bufferUsing = buffers.stream()
                    .filter(buffer -> buffer.getState() == BufferState.IDLE)
                    .findFirst().orElse(null);
            assert bufferUsing != null;
            bufferUsing.setState(BufferState.ACTIVE);
            bufferLock.unlock();
        }

        bufferUsing.add(num);

        if (bufferUsing.isFull()) {
//            System.out.println("[bufferWrite] AT write : buffer满，放入队列");
            bufferLock.lock();
            bufferUsing.setState(BufferState.WRITING);
            bufferLock.unlock();
            queue.put(bufferUsing); // 放入写队列
            bufferUsing = null;
            canPoll.release();
        }
    }

    /**
     * 写线程逻辑：不断从queue中取buffer并写入文件
     */
    @Override
    public void run() {
        try {
            finish.set(false);
            while (!finish.get() || !queue.isEmpty()) {

                canPoll.acquire();

                bufferSimulator buffer = queue.poll();
                if (buffer == null) {
                    Thread.sleep(1);
//                    System.out.println("looping "+finish.get()+" "+queue.size());
                    continue;
                }

//                System.out.println("[bufferWrite] AT run : 从队列中获取Buffer,准备写入文件");

                try {
                    while (!buffer.isEmpty()) {
                        bufferedWriter.write(buffer.poll() + "\n");
                    }
                    bufferedWriter.flush();
                } catch (IOException e) {
                    System.out.println("[bufferWriter] IOException: " + e.getMessage());
                }

                bufferLock.lock();
                buffer.setState(BufferState.IDLE);
                bufferLock.unlock();
//                System.out.println("[bufferWrite] AT run : 释放bufferAvailable");
                bufferAvailable.release();
            }
//            System.out.println("[bufferWrite] AT run : 跳出循环");
            close();
        } catch (Exception e) {
            System.out.println("[bufferWriter] Exception in run: " + e.getMessage());
        }
    }

    /**
     * 处理最后未满的buffer
     */
    private void flushRemaining() {
//        System.out.println("flushing");
        if (bufferUsing != null && !bufferUsing.isEmpty()) {
            try {
                queue.put(bufferUsing);
                bufferUsing = null;
            } catch (InterruptedException e) {
                System.out.println("[bufferWriter] flushRemaining Interrupted");
            }
        }
    }

    /**
     * 等待写线程结束并关闭文件
     */
    public void close() {
        try {
            bufferedWriter.flush();
            bufferedWriter.close();
//            System.out.println("[bufferWriter] : 成功close");
        } catch (Exception e) {
            System.out.println("[bufferWriter] close Exception: " + e.getMessage());
        }
    }
}
